/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  unitt.h
 *         \unit  unitt
 *        \brief  This is a simple unit test module for C language
 *       \author  Lamdonn
 *      \version  v1.0.0
 *      \license  GPL-2.0
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#ifndef __unitt_H
#define __unitt_H

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>

/* Version infomation */
#define UNITT_V_MAJOR                           0
#define UNITT_V_MINOR                           1
#define UNITT_V_PATCH                           0

// Type definition for a clock function
typedef uint64_t (*unitt_clock_t)();

// Type definition for a test function
typedef int (*unitt_func_t)();

// Type definition for a setup function before the test
typedef int (*unitt_setup_t)();

// Type definition for a teardown function after the test
typedef int (*unitt_teardown_t)();

// Type definition for a failure callback function
typedef int (*unitt_failcb_t)(const char* name);

// Type definition for a random input generation function
typedef int (*unitt_randin_t)();

/**
 * \brief Structure representing a test case
 */
typedef struct 
{
    const char* name;                           ///< Name of the test
    unitt_func_t func;                          ///< Pointer to the test function
    unitt_setup_t setup;                        ///< Pointer to the setup function
    unitt_teardown_t teardown;                  ///< Pointer to the teardown function
    unitt_randin_t random_input;                ///< Pointer to the random input generation function
    uint32_t tested;                            ///< Count of tests executed
    uint32_t passed;                            ///< Count of tests passed
    uint64_t time;                              ///< Total time taken for the test
} UNITT_TCASE;

/**
 * \brief Structure representing a test suite
 */
typedef struct 
{
    const char* name;                           ///< Name of the test suite
    UNITT_TCASE* tests;                         ///< Array of test cases
    uint32_t count;                             ///< Number of test cases
    unitt_clock_t clock;                        ///< Pointer to the clock function for measuring time
} UNITT;

#define UNITT_E_OK                              (0)  // Success return code
#define UNITT_E_FAIL                            (-1) // Failure return code

/**
 * \brief Assert that a condition is true
 * \param[in] condition: The condition to evaluate
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_true(int condition, const char* message);

/**
 * \brief Assert that a condition is false
 * \param[in] condition: The condition to evaluate
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_false(int condition, const char* message);

/**
 * \brief Assert that two integers are equal
 * \param[in] expected: The expected value
 * \param[in] actual: The actual value
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_equal(int expected, int actual, const char* message);

/**
 * \brief Assert that two integers are not equal
 * \param[in] expected: The expected value
 * \param[in] actual: The actual value
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_nequal(int expected, int actual, const char* message);

/**
 * \brief Assert that a pointer is NULL
 * \param[in] pointer: The pointer to evaluate
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_null(void* pointer, const char* message);

/**
 * \brief Assert that a pointer is not NULL
 * \param[in] pointer: The pointer to evaluate
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_nnull(void* pointer, const char* message);

/**
 * \brief Assert that two floats are approximately equal
 * \param[in] expected: The expected float value
 * \param[in] actual: The actual float value
 * \param[in] epsilon: The acceptable difference between the two values
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_float(float expected, float actual, float epsilon, const char* message);

/**
 * \brief Assert that two strings are equal
 * \param[in] expected: The expected string
 * \param[in] actual: The actual string
 * \param[in] message: The message to display if the assertion fails
 * \return Returns UNITT_E_OK if the assertion passes, otherwise UNITT_E_FAIL
 */
int unitt_det_string(const char* expected, const char* actual, const char* message);

/**
 * \brief Handle a test failure
 * \param[in] name: The name of the failed test
 * \return Returns UNITT_E_OK
 */
int unitt_fail_handle(const char* name);

/**
 * \brief Execute a test suite
 * \param[in] suites: Array of test suites to execute
 * \param[in] count: Number of test suites in the array
 * \param[in] failcb: Callback function to handle test failures
 * \param[in] specific: Specific test name to execute (NULL for all)
 * \param[in] filename: Output filename for results (NULL for stdout)
 * \return none
 */
void unitt_execute(UNITT* suites, uint32_t count, unitt_failcb_t failcb, const char* specific, const char* filename);

/**
 * \brief Macro to define a test case
 * \param name: Name of the test case
 */
#define UNITT_TCASE(name) { #name, name, NULL, NULL, NULL, 0, 0, 0 }

/**
 * \brief Macro to execute a set of test cases
 * \param suites: Array of test cases to execute
 */
#define UNITT_EXE(suites) unitt_execute((suites), sizeof(suites) / sizeof(suites[0]), unitt_fail_handle, 0, 0)

/**
 * \brief Macro to execute a set of test cases with additional parameters
 * \param suites: Array of test cases to execute
 * \param count: Number of test cases
 * \param mode: Execution mode
 * \param specific: Specific test name to execute
 * \param output: Output filename for results
 */
#define UNITT_EXE2(suites, count, mode, specific, output) unitt_execute(suites, count, unitt_fail_handle, specific, output)

#endif // __unit_H
